\documentclass[a4paper,10pt]{article}
\usepackage[utf8]{inputenc}
\usepackage{graphicx}
\usepackage[francais]{babel}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{mathrsfs}
\usepackage{wrapfig}
\usepackage[vlined,lined,linesnumbered,boxed,french,longend]{algorithm2e}
\usepackage{xcolor}
\usepackage[top=2.5cm, bottom=2.5cm, left=2cm, right=2cm]{geometry}
\usepackage{lmodern}
\usepackage{enumerate}
\usepackage{verbatim}
\usepackage{multirow}
\usepackage{multicol}
\usepackage{tikz}
\usepackage{listings}
\usetikzlibrary{arrows,automata,shapes}

\lstset{language=SQL, frame=shadowbox}

%partie concernant la gestion des entêtes
\usepackage{fancyhdr}
\lhead{\bsc{Projet de Compilation}}
\rhead{\bsc{Rubic}}
\renewcommand{\headrulewidth}{1px}
\lfoot{ \bsc{Enseirb-Matmeca}}
\rfoot{ \bsc{Informatique-I2}}
\renewcommand{\footrulewidth}{1px}
\pagestyle{fancy}
%fin
\begin{document}

\clearpage
\vspace*{\stretch{1}}
\begin{center}
{\huge Projet de Compilation}\\
{\footnotesize Création d'un compilateur \bsc{Rubic} vers le langage \bsc{LLVM}}\\
~\\
{\large Kévin \bsc{Gallienne} \& Sami \bsc{Lachdhaf} }
\end{center}
\vspace*{\stretch{1}}

\begin{center}
\today
\end{center}

\newpage

\newpage
\tableofcontents
\newpage

\section*{Introduction}
\addcontentsline{toc}{section}{Introduction}

Un compilateur est un programme permettant de transformer un code écrit dans un langage de programmation particulier en un autre langage. Cela permet par exemple à l'ordinateur de pouvoir interpréter un programme. Un compilateur doit donc effectuer une analyse syntaxique et sémantique. La première consiste à reconnaître des mots particuliers et la seconde permet de reconnaître des suites de mots particulières à l'aide d'une grammaire. En effet, un langage se définit principalement par sa grammaire permettant de définir les mots clés et la structure du langage.\\

L'analyse syntaxique est assurée par l'outil \texttt{lex}. Celui-ci permet permet de reconnaître des mots que l'on précise à l'aide d'expressions régulières.  Une fois un mot reconnu, une action peut être exécutée. Celle ci permet alors d'informer \texttt{yacc} le type de mot reconnu. À partir de ces informations et des règles sémantiques définies, \texttt{yacc} est capable de reconnaître des instructions particulières. Puis comme pour \texttt{lex}, on peut effectuer une action pour chaque règle ou partie de règle reconnue.\\

Pour pouvoir exécuter un programme écrit dans un langage particulier, il existe deux solutions. La première consiste à transformer directement le code d'entrée en code machine. Cette solution se révèle rapidement complexe pour des langages de haut niveau. C'est pourquoi on préférera transformer le code d'entrée dans un langage intermédiaire qui dispose déjà d'un compilateur efficace. On se propose donc de créer un compilateur permettant de transformé le langage \bsc{Rubic} en langage \bsc{LLVM}.

\section{Structure de données}
\subsection{Les tables de hachages}

  Lors de la compilation, un certain nombre d'informations doivent être stockées pour pouvoir déceler les erreurs du programmeur. Ces données étant utilisées potentiellement un grand nombre de fois, leur accès doit être rapide pour éviter un temps de compilation trop long. C'est pourquoi on privilégie l'utilisation de table de hachage.
  
\subsubsection{La fonction de hachage}

  Selon le nombre de données à stockées, la taille de la table peut être plus ou moins grande mais celle-ci doit avoir une limite. Il y a donc des risques de collisions. Ce risque dépend principalement de la fonction de hachage choisie. En effet, pour prendre un cas extrême,  si la fonction de hachage est constante, il n'y a que des collisions. Pour éviter les collisions, la fonction de hachage doit être au moins injective. Pour ce faire, on choisit une fonctions de hachage prenant le plus de variables distinctes possible. Selon la fonction de hachage, on peut aussi faire varier la taille du tableau.\\
  
  Comme les clefs que nous manipulerons sont toutes des chaînes de caractères, notre fonction de hachage se base sur les caractères composant la chaîne. Ces chaînes représentent majoritairement les identifiants des variables, des fonctions ou des classes manipulées. Ces identifiants étant souvent court et varié, nous nous basons  sur la taille du tableau pour assurer un minimum de collisions.\\
  
  En cas de collisions, il est possible d'ajouter une gestion des collisions pour éviter la perte de données. Cette gestion passe par l'introduction de listes chaînées. Chaque cellule du tableau ne contiendra plus une seule entité mais une liste. Il conviendra alors d'utiliser un système  de d'identification pour savoir quelle entité correspond à quelle symbole.
  
\subsubsection{Table des types et des classes}

  La principale information à stockée concerne le type de chaque variable. En effet, il convient de définir le type de chaque variable au moment de sa déclaration. Cette donnée est essentielle pour le respect des opérations effectuées. Dans le langage \bsc{Rubic} le typage s'effectue dynamiquement. De ce fait, il convient de remarquer qu'il ne peut y avoir de variables non initialisés contrairement à des langages comme le C car le type d'une variable s'appuie sur la première affectation effectuée. Par exemple, l'instruction \texttt{x = 2} définit le type de la variable \texttt{x} comme le type de \texttt{2} , en l'occurrence un entier. Toute les variables ne possédant pas de type défini correspondent à des variables non déclarées.
  
  Nous considérons qu'une variable \texttt{Rubic} ne peut présenter que six types : entier, double, chaîne de caractères, fonction, classe et objet. De ce fait, il suffit d'établir la correspondance entre variable et type de la manière suivante:
\begin{center}\begin{tabular}{|c|c|c|c|c|c|}
\hline
$x$ & $y$ & $func$ & ... & $A$ & a\\
\hline
entier & double & fonction & ... & classe & A\\
\hline
\end{tabular}\end{center}

Il faut remarquer qu'il faut connaître le type des objets, c'est à dire savoir de quelle classe l'objet est une instance pour connaître les attributs auxquels il a accès mais aussi les méthodes qu'il peut appeler. C'est pourquoi on ne met pas simple \og objet\fg.\\

De même pour les classes, on introduit une autre table de hachage mais cette fois indexée par le nom des classes. Cette table contiendra à la fois les attributs et les méthodes de la classe correspondantes. Au moment de l'instanciation d'un objet, la méthode \texttt{initialize} de la classe est appelée entraînant l'initialisation (obligatoire) des attributs de l'objet. En d'autre termes pour chaque attribut de la classe, une entrée \texttt{objet.attribut} est introduite dans la table des types.
\begin{center}\begin{tabular}{|c|c|c|c|}
\hline
$A$ & $B$ & $C$ & ... \\
\hline
attr1; attr2; attr3; ...& attr1; attr1; attr2; attr3; ... & attr1; attr2; attr3; ... & ... \\
\hline
\end{tabular}
\end{center}

\begin{center}\begin{tabular}{|c|c|c|c|}
\hline
$A$ & $B$ & $C$ & ... \\
\hline
meth1; meth2; meth3; ...& meth1; meth1; meth2; meth3; ... & meth1; meth2; meth3; ... & ... \\
\hline
\end{tabular}
\end{center}

Il convient de noter que seule l'identifiant des méthodes est stocké dans la table. Le code en lui-même devra être redéfini à chaque instanciation de la classe. Ce problème sera abordé plus en détail dans la partie dédiée.

\subsubsection{Table des fonctions}
\subsection{Portée des variables}

Pour que la table des symboles soit cohérente,  il convient de différencier les variables  suivant l'endroit ou elles sont définies. Dans le cas contraire, les variables ne peuvent présenter le même identifiant. C'est pourquoi il convient de trouver un système permettant de différencier les différents contextes de déclarations.  

\subsubsection{Chaînage des tables des symboles}

Une première solution consiste à manipuler différente tables des symboles pour chaque nouveau contexte. Il existe alors une hiérarchie dans ces différentes tables car un bloc doit pouvoir avoir accès aux variables du bloc englobant mais pas le contraire. Cette notion de hiérarchie peut être représenter par un simple chaînage.\\

Ainsi au début du programme, on crée une première table des symboles qui va correspondre aux variables globales et donc accessibles par tous les blocs. On parcours alors le code à compiler et à chaque entré de bloc, on crée une nouvelle table qu'on chaîne à la précédente. Enfin à la sortie d'un bloc on détruit la table qui se trouve en bout de chaîne.\\

Pour rechercher le type d'une variable, on commence par regarder dans la table courante qui se trouve au bout de la chaîne. Si elle n'est pas présente, on regarde dans la table précédente. La première occurrence de la variable rencontré sera retourner. On peut alors remonter une à une chacune des tables pour trouver la bonne valeur. Si la variable n'est présente dans aucune table, cela signifie qu'elle n'est pas définit. Il faut alors retourner une erreur.\\

Par exemple considérons le code suivant écrit dans un pseudo-code. Chaque bloc est noté $B_{i}$ et chaque table de hachage correspondante $TB_{i}$. On peut alors construire l'arbre des tables construite lors du traitement du code d'entrée. Bien sur, dans le pire des cas, seule une branche est présente en mémoire à n'importe quel moment.\\

Cette méthode présente l'intérêt de pouvoir effectuer des recherches rapides et intuitive. Cependant, une telle représentation devient très vite volumineuse sachant qu'on peut imbriquer autant de bloc que l'on souhaite et qu'une table de hachage est par nature déjà volumineuse. Comme n'ont pas beaucoup de blocs imbriqués, nous avons choisi de ne pas retenir cette solution.
 
\begin{figure}[h]
\begin{wrapfigure}[0]{r}{13cm}
\centering
\includegraphics[scale=0.35]{table.jpg}
\end{wrapfigure}
~\\

\begin{verbatim}
                        ...
                        B1{
                            ...
                            B2{...} 
                            B3{...}
                        }
                        B4{....}
\end{verbatim}
  \caption{Hierarchie des tables de hachage}
\end{figure}

\subsubsection{Structure contexte}

Une autre manière de travailler consiste à considérer une variable contexte qui serait une chaîne de caractère. Celle si représenterait l'environnement courant. Chaque bloc aurait ainsi une dénomination différente à l'intérieur de chaque bloc englobant. À partir de là chaque variable est préfixée par son contexte permettant ainsi de distinguer chaque variable.\\

Ainsi si on considère la variable $x$ déclarée dans un bloc $B_1$ et aussi dans un bloc $B_2$ dans le code de la partie précédente, dans la table des symbole il y aura une entrée $B_{1}.x$ et une entrée $B_{1}.B_{2}.x$. Ainsi on ne considère plus une chaîne de tables de hachages mais une chaîne de chaînes de caractères. La fonction de recherche est la même que la précédente à l'exception que le parcours s'effectue sur cette nouvelle chaîne et non plus sur les tableaux de hachages. Ceci rend la structure beaucoup plus légère car on se contente d'une unique table de hachage. C'est pourquoi nous avons retenu cette solution.\\

Pour représenté cette structure on utilise tout simplement un tableau, qui contiendra les identifiants des différents blocs dans l'ordre dans lequel ils ont été rencontrés, et un entier pour connaître la position courante dans le tableau et donc du contexte. À l'aide ces deux seuls champs on peut construire le contexte courant.

\subsection{La structure de parcours}

Au delà de ce problème de contexte, on doit stocker des informations lors du parcours à proprement dit. En effet, l'analyse \texttt{yacc} se faisant de manière ascendante, il faut pouvoir mémoriser les opérations effectuées au plus bas niveau. Comme ces informations sont multiples il est nécessaire d'introduire une structure. Cette structure correspondra au type de chaque lexème identifié.\\

La première chose à récupérer est l'ensemble des instructions \bsc{LLVM} déduite du code \bsc{Rubic}. Ainsi notre structure disposera d'un champ où seront concaténées les instructions au fur et à mesure. Par exemple si on considère l'instruction \texttt{a = b + c}, il faut remarquer que \texttt{b} et \texttt{c} peuvent être eux même issu d'un calcul. La première étape consiste donc à écrire \texttt{a.inst = b.inst + c.inst} où \texttt{+} désigne l'opérateur de concaténation.\\

De plus pour effectuer réellement l'affectation, il est nécessaire de connaître le registre dans lequel est stocké le résultat de la somme de \texttt{b} et \texttt{c}. Il faut donc introduire un nouveau champ, qui représentera le registre dans lequel est stocké la dernière valeur calculée. On doit alors effectuer  \texttt{a.reg = (b+c).reg} la somme s'effectuant en amont de l'affectation.\\

Enfin, il faut connaître le type de chaque instruction pour pouvoir, au moment de l'affectation, ajouter la bonne valeur à la table de hachage. Cela se justifie d'autant plus que certaines instructions ne possèdent pas d'identifiants propres (calculs intermédiaires) et que leurs types est susceptible d'être modifié.

\section{Transformation du langage \bsc{Rubic} vers \bsc{LLVM}}
Le language \bsc{LLVM} est le language utilisé par le compilateur \bsc{LLVM}, c'est un language bas niveau, ce qui implique qu'il n'est pas possible de représenter directement des structures présentent dans le \bsc{Ruby}/\bsc{Rubic}, tel que les boucles \texttt{while} et \texttt{for}. 

\subsection{Traduction des structures de contrôle}
\subsubsection{Calculs basiques}

Le language \bsc{LLVM} possède quatre types de base : le type \texttt{ix}, qui représente les entiers codés sur x bits, 
par exemple \texttt{i32} pour les entiers sur 32 bits ou \texttt{i8} pour les entiers sur 8 bits; les types \texttt{float} et \texttt{double} pour les réels codés sur 32 et 64 bits, et le type \texttt{void}.
Pour le type \texttt{int} du \bsc{Rubic}, c'est le type \texttt{i32} du \bsc{LLVM} qui a été choisi, et pour le type 
\texttt{float} de \bsc{Rubic}, nous avons choisi le type \texttt{double}, celui ci étant plus 
facilement manipulable que le type \texttt{float}.\\

Certaines instructions demandent une conversion de type, en particulier le passage de \texttt{int} à à \texttt{double}. 
C'est la fonction \texttt{uitofp} de \bsc{LLVM} qui nous permet ceci.
Par exemple, l'instruction \texttt{\%1 = uitofp i32 5 to double} stocke dans le registre \texttt{\%1} la conversion 
de l'entier 5 en double.\\

\begin{center}
\begin{tabular}{|c|c|c|c|}
  \hline
  Instruction & Syntaxe & Rubic & LLVM \\
  \hline
  \texttt{add}/\texttt{fadd} & \texttt{<result> = add i32 <op1>, <op2>} & x = 1 + 1 & \%x = add i32 1, 1\\
  & \texttt{<result> = fadd double <op1>, <op2>}  & & \\
  \hline
  \texttt{sub}/\texttt{fsub} & \texttt{<result> = sub i32 <op1>, <op2>} & x = 1 - 1 & \%x = sub i32 1, 1\\
  & \texttt{<result> = fsub double <op1>, <op2>}  & & \\
  \hline
  \texttt{mul}/\texttt{fmul} & \texttt{<result> = mul i32 <op1>, <op2>} & x = 1 * 1 & \%x = mul i32 1, 1\\
  & \texttt{<result> = fmul double <op1>, <op2>}  & & \\
  \hline
  \texttt{sdiv}/\texttt{fdiv} & \texttt{<result> = sdiv i32 <op1>, <op2>} & x = 1 / 1 & \%x = sdiv i32 1, 1\\
  & \texttt{<result> = fdiv double <op1>, <op2>}  & & \\
  \hline
\end{tabular}
\end{center}

Le language \bsc{LLVM} ne permet pas d'utiliser directement un opérateur dans un autre. Ainsi, 
pour calculer la valeur de 1 + 1 + 1, il faut passer par deux opérations \texttt{add}, 
l'instruction \texttt{add i32 (add i32 1, 1), 1} n'est pas correcte. Il faut passer par des registres temporaires :
\begin{verbatim}
        %1 = add i32 1, 1
        %2 = add i32 %1, 1 
\end{verbatim}

\subsubsection{Branchement conditionnel}

En language \bsc{LLVM}, les conditions sont représentées par des branchements conditionnels. Ces branchements sont basés sur un système de \texttt{goto} et de \texttt{labels}. Ces branchements peuvent être représentés comme ceci :
\begin{verbatim}
        si cond goto label1 sinon goto label2
\end{verbatim}
L'instruction \bsc{LLVM} représentant un branchement conditionnel est la suivante : 
\begin{verbatim}
        br i1 <cond>, label <iftrue>, label <iffalse>
\end{verbatim}

\noindent Par exemple, l'instruction \bsc{Rubic} suivante
\begin{verbatim}
        if 3 > 4 then
            i = 2
        else
            i = 1
        end
        ...
\end{verbatim}
se code en \bsc{LLVM} de la manière suivante
\begin{verbatim}
        %cond = icmp sgt i32 3, 4
        br i1 %cond, label %l1, label %l2
        l1:
        %i = 2
        br label %l3
        l2:
        %i = 1
        br label %l3
        l3:
        ....
\end{verbatim}

L'instruction \texttt{br label \%l3} est un goto conduisant à la fin de l'instruction, afin que un seul 
des deux blocs ne soit parcourus. De plus, \texttt{i1 \%cond} est un booléen, dont la valeur est déterminée par 
l'instruction \texttt{icmp sgt i32 3, 4}. \texttt{icmp} est l'instruction logique permettant de comparer deux entiers, 
son équivalent pour les nombres flottants est \texttt{fcmp}.

\subsubsection{Les boucles while et for}

La représentation des boucles \texttt{while} et \texttt{for} en \bsc{LLVM} est plus compliquée. En effet, les registres 
\bsc{LLVM} ne peuvent être affectés qu'une seule fois, il faut donc trouver un moyen 
d'exprimer les compteurs pour les boucles. \\

Pour s'abstraire de ces problèmes d'affectation et éviter des problèmes d'ambiguïtés entre les variables pointeurs et non-pointeurs, 
nous avons décidé de déclarer toutes nos variables comme des pointeurs. Toute valeur est stockée dans un registre sous la forme d'un pointeur.
Pour manipuler ces pointeurs, on utilise les fonctions \texttt{load} et \texttt{store} de \bsc{LLVM} qui permettent respectivement de charger la valeur stocké dans un pointeur et de stocker une valeur dans un pointeur. \\

De plus, puisque le \bsc{LLVM} ne possède pas de boucles, il faut là aussi utiliser les \texttt{goto} et les 
\texttt{label} par exemple, le code \bsc{Rubic} suivant 
\begin{verbatim}
        i = 0.0
        while i < 5.0 do
            i = i + 1.0
        end
\end{verbatim}
se code en \bsc{LLVM} de la manière suivante :
\begin{verbatim}
        %1 = alloca double
        store double 0.0, double* %1
        %i = alloca double
        %2 = load double* %1
        store double %2, double* %i
\end{verbatim}

Ces lignes permettent l'initialisation de \texttt{i}, stocké sous forme de pointeur (\texttt{i = 0}) pour permettre sa modification au cours de la boucle.
\begin{verbatim}
        %3 = alloca double
        store double 5.0, double* %3
        %4 = load double* %i
        %5 = load double* %3
        %6 = fcmp olt double %4, %5
        %7 = alloca i1
        store i1 %6, i1* %7
        %8 = load i1* %7
\end{verbatim}

Ces lignes permettent de déclarer une première fois la condition \texttt{i < 5}. La valeur de cette expression est ponctuellement stockée dans un registre.
\begin{verbatim}
        br i1 %8, label %a0, label %a1
        a0:
        %9 = alloca double
        store double 1.0, double* %9
        %10 = load double* %i
        %11 = load double* %9
        %12 = fadd  double %10, %11
        %13 = alloca double
        store double %12, double* %13
        %14 = load double* %13
        store double %14, double* %i
\end{verbatim}
On donne à \texttt{i} sa nouvelle valeur (\texttt{i = i + 1.0}).
\begin{verbatim}
        %15 = alloca double
        store double 5.0, double* %15
        %16 = load double* %i
        %17 = load double* %15
        %18 = fcmp olt double %16, %17
        %19 = alloca i1
        store i1 %18, i1* %19
        %20 = load i1* %19
\end{verbatim}
On réévalue la condition. 
\begin{verbatim}
        br i1 %20, label %a0, label %a1
        a1:
        .... (suite du programme)
\end{verbatim}

\subsection{Problème de traduction}
\subsubsection{Gestion des fonctions}

La gestion des fonctions devient très vite problématique car le type des variables d'entrées n'est pas défini en \bsc{Rubic} alors qu'en \bsc{LLVM} toutes les variables doivent être typées explicitement. Donc, il est claire que chaque fonction doit être implémentée en plusieurs versions selon les types d'entrée. Ces différentes sont différenciée en postfixant le nom de la fonction par le type des variables d'entrée. Par exemple, on peut avoir en \bsc{LLVM} :
\begin{verbatim}
            A.func.i32.32(i32 a, i32 b)
            A.func.float.32(float a, i32 b)
            ...
\end{verbatim}

On sait alors sans ambiguïté quelle méthode appeler à quel moment selon le type des variables. De plus on évite ainsi les conflits de notation pouvant entraîner des collision dans la table des symboles.\\

Cependant pour générer le corps de la fonction il est nécessaire de connaître le type des variables. Or, lors du traitement du code par \texttt{yacc}, chaque action sémantique ne peut être exécutée qu'une seule fois. Donc il n'est pas possible de régénérer ce code pour chaque type possible. On ne peut pas retraiter nous même le code car certaine instruction peuvent manquer (conversion d'entiers vers des floats).

\subsubsection{Problème particulier des conditions}

Un cas particulier des conditions pose problème. En effet, lorsque l'on retourne une valeur dans les différentes branches de la condition, il faut retourner une valeur dans chacune d'entre elles. Cette règle est valable aussi bien en \bsc{Rubic} qu'en \bsc{LLVM}. Or dans une structure conditionnelle du type \texttt{if then else}, il y a un dernier label permettant la sortie du branchement. Après ce label un \texttt{return} est nécessaire pour respecter la syntaxe du langage même si ce n'est pas le cas en \texttt{Rubic}.\\

Une solution possible à ce problème de mémoriser la présence ou non d'un \texttt{return} dans un des bloc pour savoir si un \texttt{return} est nécessaire ou non après le label. On ne peut en mettre un systématiquement  car on risquerait de fausser la nature du code de la fonction. En effet, des instructions peuvent suivre la structure \texttt{if then else}. Celles-ci seraient ignorées si on ajouter un \texttt{return}.

\subsubsection{Imbrication et gestion des registres}

Comme indiqué précédemment, nous utilisons des registres pour effectuer nos calculs. Cependant le langage \texttt{LLVM} impose des restrictions quant à l'utilisation de ces registres. Tout d'abord comme toute variable, ils ne peuvent être affectés qu'une seule fois. L'utilisation de ces registres doit se faire dans l'ordre croissant. En outre, il est impossible d'utiliser le registre $i$ si le registre $i-1$ n'a pas encore été utilisé même les instruction sont correctes.\\

Cette règle complique la gestion de ces registres complique l'utilisation de structure imbriqué car on ne connaît pas par avance les instructions effectuées et donc les registres utilisés. Ce phénomène se produit également lors de l'utilisation des labels.\\

Pour une meilleur gestion de ces grandeurs, il aurait été judicieux d'utiliser une autre manière de gérer les calculs intermédiaire mais aussi de trouver une autre numérotation des labels. Cela aurait permis l'imbrication de structures.

\subsection{Gestion des classes}
\subsubsection{Représentation d'une classe et d'un objet}

L'un des aspect les plus important d'un langage orienté objet consiste en l'utilisation de classes et d'objets. Pour représenter de telles entités dans un langage impératif, il est nécessaire d'introduire de nouvelles structures de données.\\

Un objet est représenté par ses attributs. Au moment de son instanciation, il est donc nécessaire de définir ses dis attributs. Il faut donc initialiser ces variables. Pour cela on doit tenir la liste des attribut de la classe afin de s'y référer. Une classe est donc représenter par une liste d'attributs et uniquement d'attributs. Puis on parcourt cette liste pour déclarer les attributs de l'objet en question ($O.attribut_i$) puis on appelle la fonction $initialize$ pour les initialiser et les ajouter à la table des symboles.

\subsubsection{Appel d'une méthode sur un objet particulier}

Une classe définit un schéma de conception d'un objet par l'intermédiaire de ses attributs et de ses méthodes. Le problème ici se révèle similaire à celui des fonctions. En effet, les méthodes peuvent être amenées à modifier l'objet qui les appellent. Dans le cas contraire elle se comporte comme des fonctions classiques. Cependant on ne connaît pas par avance les objet qui seront instanciés au moment de la définition de la classe.\\

Pour résoudre ce problème, une solution consiste à passer, en plus des paramètre déjà défini de la fonction, les différents attributs de l'objet en question. En effet, comme l'état d'un objet est entièrement défini par ses attributs, cette est similaire à passer l'objet entier en paramètre.\\

Cependant il faut convenir que ce phénomène ne doit se produire qu'au moment de la définition d'une classe. Il faut donc se référer au contexte pour connaître l'attitude à arborer.

\section*{Conclusion}
\addcontentsline{toc}{section}{Conclusion}

  En conclusion, nous avons réalisé un compilateur qui ne reconnaît pour le moment que des programmes basiques du langage \texttt{Rubic}. Cela nous a permis néanmoins de découvrir les différents outils permettant d'établir une analyse aussi bien sémantique que syntaxique d'un langage pour pouvoir finalement le compiler. Cependant ces outils sont parfois peu adapté à la réalisation de certains équivalents. De plus, il est parfois difficile d'établir une compilation d'un langage à un autre lorsque ceux-ci sont très différents et que nous ne sommes pas familiarisé avec l'un des deux.\\

\end{document}